{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Main Driver Notebook for Training Graph NNs on OGBL-COLLAB for Edge Classification"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### MODELS\n",
    "- GatedGCN \n",
    "- GCN \n",
    "- GAT \n",
    "- GraphSage \n",
    "- GIN  \n",
    "- MoNet  \n",
    "- MLP \n",
    "- Matrix Factorization (MF)\n",
    "\n",
    "### DATASET\n",
    "- OGBL-COLLAB\n",
    "\n",
    "### TASK\n",
    "- Edge Classification"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "    IMPORTING LIBS\n",
    "\"\"\"\n",
    "import dgl\n",
    "\n",
    "import numpy as np\n",
    "import os\n",
    "import socket\n",
    "import time\n",
    "import random\n",
    "import glob\n",
    "import argparse, json\n",
    "import pickle\n",
    "\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "import torch.optim as optim\n",
    "from torch.utils.data import DataLoader\n",
    "\n",
    "from tensorboardX import SummaryWriter\n",
    "from tqdm import tqdm\n",
    "\n",
    "class DotDict(dict):\n",
    "    def __init__(self, **kwds):\n",
    "        self.update(kwds)\n",
    "        self.__dict__ = self\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# \"\"\"\n",
    "#     AUTORELOAD IPYTHON EXTENSION FOR RELOADING IMPORTED MODULES\n",
    "# \"\"\"\n",
    "\n",
    "def in_ipynb():\n",
    "    try:\n",
    "        cfg = get_ipython().config \n",
    "        return True\n",
    "    except NameError:\n",
    "        return False\n",
    "    \n",
    "notebook_mode = in_ipynb()\n",
    "print(notebook_mode)\n",
    "\n",
    "if notebook_mode == True:\n",
    "    %load_ext autoreload\n",
    "    %autoreload 2\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "    IMPORTING CUSTOM MODULES/METHODS\n",
    "\"\"\"\n",
    "from nets.COLLAB_edge_classification.load_net import gnn_model # import all GNNS\n",
    "from data.data import LoadData # import dataset\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "    GPU Setup\n",
    "\"\"\"\n",
    "def gpu_setup(use_gpu, gpu_id):\n",
    "    os.environ[\"CUDA_DEVICE_ORDER\"] = \"PCI_BUS_ID\"\n",
    "    os.environ[\"CUDA_VISIBLE_DEVICES\"] = str(gpu_id)  \n",
    "\n",
    "    if torch.cuda.is_available() and use_gpu:\n",
    "        print('cuda available with GPU:',torch.cuda.get_device_name(0))\n",
    "        device = torch.device(\"cuda\")\n",
    "    else:\n",
    "        print('cuda not available')\n",
    "        device = torch.device(\"cpu\")\n",
    "    return device\n",
    "\n",
    "\n",
    "# select GPU or CPU\n",
    "#use_gpu = True; gpu_id = 0; device = None # default GPU\n",
    "use_gpu = False; gpu_id = -1; device = None # CPU\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# \"\"\"\n",
    "#     USER CONTROLS\n",
    "# \"\"\"\n",
    "if notebook_mode == True:\n",
    "    \n",
    "    # MODEL_NAME = '3WLGNN'\n",
    "    # MODEL_NAME = 'RingGNN'\n",
    "    # MODEL_NAME = 'MF'\n",
    "    # MODEL_NAME = 'MLP'\n",
    "    # MODEL_NAME = 'MLP'\n",
    "    # MODEL_NAME = 'GAT'\n",
    "    MODEL_NAME = 'GatedGCN'\n",
    "    # MODEL_NAME = 'GAT'\n",
    "    # MODEL_NAME = 'GraphSage'\n",
    "    # MODEL_NAME = 'DiffPool'\n",
    "    # MODEL_NAME = 'GIN'\n",
    "\n",
    "    DATASET_NAME = 'OGBL-COLLAB'\n",
    "\n",
    "    out_dir = 'out/COLLAB_edge_classification/debug/'\n",
    "    root_log_dir = out_dir + 'logs/' + MODEL_NAME + \"_\" + DATASET_NAME + \"_\" + time.strftime('%Hh%Mm%Ss_on_%b_%d_%Y')\n",
    "    root_ckpt_dir = out_dir + 'checkpoints/' + MODEL_NAME + \"_\" + DATASET_NAME + \"_\" + time.strftime('%Hh%Mm%Ss_on_%b_%d_%Y')\n",
    "\n",
    "    print(\"[I] Loading data (notebook) ...\")\n",
    "    dataset = LoadData(DATASET_NAME)\n",
    "    print(\"[I] Finished loading.\")\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# \"\"\"\n",
    "#     PARAMETERS\n",
    "# \"\"\"\n",
    "if notebook_mode == True:\n",
    "    \n",
    "#     MODEL_NAME = 'MF'\n",
    "    MODEL_NAME = 'GatedGCN'\n",
    "    \n",
    "    n_heads = -1\n",
    "    edge_feat = False\n",
    "    pseudo_dim_MoNet = -1\n",
    "    kernel = -1\n",
    "    gnn_per_block = -1\n",
    "    embedding_dim = -1\n",
    "    pool_ratio = -1\n",
    "    n_mlp_GIN = -1\n",
    "    gated = False\n",
    "    self_loop = False\n",
    "    max_time = 12\n",
    "    layer_type = 'dgl'\n",
    "    num_embs = -1\n",
    "    pos_enc = True\n",
    "    #pos_enc = False\n",
    "    pos_enc_dim = 10\n",
    "\n",
    "    \n",
    "    if MODEL_NAME == 'MF':\n",
    "        seed=41; epochs=500; batch_size=32*1024; init_lr=0.01; lr_reduce_factor=0.5; lr_schedule_patience=10; min_lr = 1e-5; weight_decay=0\n",
    "        L=0; hidden_dim=256; out_dim=hidden_dim; num_embs=235868;\n",
    "    \n",
    "    if MODEL_NAME == 'MLP':\n",
    "        seed=41; epochs=500; batch_size=32*1024; init_lr=0.001; lr_reduce_factor=0.5; lr_schedule_patience=10; min_lr = 1e-5; weight_decay=0\n",
    "        L=3; hidden_dim=80; out_dim=hidden_dim; dropout=0.0; readout='mean'; gated = False  # Change gated = True for Gated MLP model\n",
    "    \n",
    "    if MODEL_NAME == 'GCN':\n",
    "        seed=41; epochs=500; batch_size=32*1024; init_lr=0.001; lr_reduce_factor=0.5; lr_schedule_patience=10; min_lr = 1e-5; weight_decay=0\n",
    "        L=3; hidden_dim=74; out_dim=hidden_dim; dropout=0.0; readout='mean';\n",
    "        \n",
    "    if MODEL_NAME == 'GraphSage':\n",
    "        seed=41; epochs=500; batch_size=32*1024; init_lr=0.001; lr_reduce_factor=0.5; lr_schedule_patience=10; min_lr = 1e-5; weight_decay=0\n",
    "        L=3; hidden_dim=38; out_dim=hidden_dim; dropout=0.0; readout='mean'; layer_type='edgefeat'\n",
    "\n",
    "    if MODEL_NAME == 'GAT':\n",
    "        seed=41; epochs=500; batch_size=32*1024; init_lr=0.001; lr_reduce_factor=0.5; lr_schedule_patience=10; min_lr = 1e-5; weight_decay=0\n",
    "        L=3; n_heads=3; hidden_dim=19; out_dim=n_heads*hidden_dim; dropout=0.0; readout='mean'; layer_type='dgl'\n",
    "    \n",
    "    if MODEL_NAME == 'GIN':\n",
    "        seed=41; epochs=500; batch_size=32*1024; init_lr=0.001; lr_reduce_factor=0.5; lr_schedule_patience=10; min_lr = 1e-5; weight_decay=0\n",
    "        L=3; hidden_dim=60; out_dim=hidden_dim; dropout=0.0; readout='mean';\n",
    "        \n",
    "    if MODEL_NAME == 'MoNet':\n",
    "        seed=41; epochs=500; batch_size=32*1024; init_lr=0.001; lr_reduce_factor=0.5; lr_schedule_patience=10; min_lr = 1e-5; weight_decay=0\n",
    "        L=3; hidden_dim=53; out_dim=hidden_dim; dropout=0.0; readout='mean';\n",
    "        \n",
    "    if MODEL_NAME == 'GatedGCN':\n",
    "        seed=41; epochs=500; batch_size=32*1024; init_lr=0.001; lr_reduce_factor=0.5; lr_schedule_patience=10; min_lr = 1e-5; weight_decay=0\n",
    "        L=3; hidden_dim=35; out_dim=hidden_dim; dropout=0.0; readout='mean'; edge_feat = False; layer_type='edgereprfeat'\n",
    "        \n",
    "    # generic new_params\n",
    "    net_params = {}\n",
    "    net_params['device'] = device\n",
    "    net_params['in_dim'] = dataset.graph.ndata['feat'].shape[-1]\n",
    "    net_params['in_dim_edge'] = dataset.graph.edata['feat'].shape[-1]\n",
    "    net_params['residual'] = True\n",
    "    net_params['hidden_dim'] = hidden_dim\n",
    "    net_params['out_dim'] = out_dim\n",
    "    num_classes = 1\n",
    "    net_params['n_classes'] = num_classes\n",
    "    net_params['n_heads'] = n_heads\n",
    "    net_params['L'] = L  # min L should be 2\n",
    "    net_params['readout'] = \"mean\"\n",
    "    net_params['layer_norm'] = True\n",
    "    net_params['batch_norm'] = True\n",
    "    net_params['in_feat_dropout'] = 0.0\n",
    "    net_params['dropout'] = 0.0\n",
    "    net_params['edge_feat'] = edge_feat\n",
    "    net_params['self_loop'] = self_loop\n",
    "    net_params['layer_type'] = layer_type\n",
    "    \n",
    "    # for MF\n",
    "    net_params['num_embs'] = num_embs\n",
    "    \n",
    "    # for MLPNet \n",
    "    net_params['gated'] = gated\n",
    "    \n",
    "    # specific for MoNet\n",
    "    net_params['pseudo_dim_MoNet'] = 2\n",
    "    net_params['kernel'] = 3\n",
    "    \n",
    "    # specific for GIN\n",
    "    net_params['n_mlp_GIN'] = 2\n",
    "    net_params['learn_eps_GIN'] = True\n",
    "    net_params['neighbor_aggr_GIN'] = 'sum'\n",
    "    \n",
    "    # specific for graphsage\n",
    "    net_params['sage_aggregator'] = 'maxpool'   \n",
    "    \n",
    "    # specific for pos_enc_dim\n",
    "    net_params['pos_enc'] = pos_enc\n",
    "    net_params['pos_enc_dim'] = pos_enc_dim\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "    VIEWING MODEL CONFIG AND PARAMS\n",
    "\"\"\"\n",
    "def view_model_param(MODEL_NAME, net_params):\n",
    "    model = gnn_model(MODEL_NAME, net_params)\n",
    "    total_param = 0\n",
    "    print(\"MODEL DETAILS:\\n\")\n",
    "    print(model)\n",
    "    for param in model.parameters():\n",
    "        # print(param.data.size())\n",
    "        total_param += np.prod(list(param.data.size()))\n",
    "    print('MODEL/Total parameters:', MODEL_NAME, total_param)\n",
    "    return total_param\n",
    "\n",
    "\n",
    "if notebook_mode == True:\n",
    "    view_model_param(MODEL_NAME, net_params)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "    TRAINING CODE\n",
    "\"\"\"\n",
    "\n",
    "def train_val_pipeline(MODEL_NAME, dataset, params, net_params, dirs):\n",
    "    t0 = time.time()\n",
    "    per_epoch_time = []\n",
    "        \n",
    "    DATASET_NAME = dataset.name\n",
    "    \n",
    "    #assert net_params['self_loop'] == False, \"No self-loop support for %s dataset\" % DATASET_NAME\n",
    "    \n",
    "    if MODEL_NAME in ['GatedGCN']:\n",
    "        if net_params['pos_enc']:\n",
    "            print(\"[!] Adding graph positional encoding\",net_params['pos_enc_dim'])\n",
    "            dataset._add_positional_encodings(net_params['pos_enc_dim'])\n",
    "            print('Time PE:',time.time()-t0)\n",
    "        \n",
    "    graph = dataset.graph\n",
    "    \n",
    "    evaluator = dataset.evaluator\n",
    "    \n",
    "    train_edges, val_edges, val_edges_neg, test_edges, test_edges_neg = dataset.train_edges, dataset.val_edges, dataset.val_edges_neg, dataset.test_edges, dataset.test_edges_neg\n",
    "        \n",
    "    root_log_dir, root_ckpt_dir, write_file_name, write_config_file = dirs\n",
    "    device = net_params['device']\n",
    "    \n",
    "    # Write the network and optimization hyper-parameters in folder config/\n",
    "    with open(write_config_file + '.txt', 'w') as f:\n",
    "        f.write(\"\"\"Dataset: {},\\nModel: {}\\n\\nparams={}\\n\\nnet_params={}\\n\\n\\nTotal Parameters: {}\\n\\n\"\"\"\\\n",
    "                .format(DATASET_NAME, MODEL_NAME, params, net_params, net_params['total_param']))\n",
    "        \n",
    "    log_dir = os.path.join(root_log_dir, \"RUN_\" + str(0))\n",
    "    writer = SummaryWriter(log_dir=log_dir)\n",
    "\n",
    "    # setting seeds\n",
    "    random.seed(params['seed'])\n",
    "    np.random.seed(params['seed'])\n",
    "    torch.manual_seed(params['seed'])\n",
    "    if device.type == 'cuda':\n",
    "        torch.cuda.manual_seed(params['seed'])\n",
    "    \n",
    "    print(\"Graph: \", graph)\n",
    "    print(\"Training Edges: \", len(train_edges))\n",
    "    print(\"Validation Edges: \", len(val_edges) + len(val_edges_neg))\n",
    "    print(\"Test Edges: \", len(test_edges) + len(test_edges_neg))\n",
    "\n",
    "    model = gnn_model(MODEL_NAME, net_params)\n",
    "    model = model.to(device)\n",
    "\n",
    "    optimizer = optim.Adam(model.parameters(), lr=params['init_lr'], weight_decay=params['weight_decay'])\n",
    "    scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='max',\n",
    "                                                     factor=params['lr_reduce_factor'],\n",
    "                                                     patience=params['lr_schedule_patience'],\n",
    "                                                     verbose=True)\n",
    "    \n",
    "    epoch_train_losses = []\n",
    "    epoch_train_hits, epoch_val_hits = [], [] \n",
    "    \n",
    "    if MODEL_NAME in ['RingGNN', '3WLGNN']:\n",
    "        raise NotImplementedError # gave OOM while preparing dense tensor\n",
    "    else:\n",
    "        # import train functions for all other GCNs\n",
    "        from train.train_COLLAB_edge_classification import train_epoch_sparse as train_epoch, evaluate_network_sparse as evaluate_network\n",
    "    \n",
    "    # At any point you can hit Ctrl + C to break out of training early.\n",
    "    try:\n",
    "        monet_pseudo = None\n",
    "        if MODEL_NAME == \"MoNet\":\n",
    "            print(\"\\nPre-computing MoNet pseudo-edges\")\n",
    "            # for MoNet: computing the 'pseudo' named tensor which depends on node degrees\n",
    "            us, vs = graph.edges()\n",
    "            # to avoid zero division in case in_degree is 0, we add constant '1' in all node degrees denoting self-loop\n",
    "            monet_pseudo = [ \n",
    "                [1/np.sqrt(graph.in_degree(us[i])+1), 1/np.sqrt(graph.in_degree(vs[i])+1)] \n",
    "                    for i in range(graph.number_of_edges())\n",
    "            ]\n",
    "            monet_pseudo = torch.Tensor(monet_pseudo)\n",
    "        \n",
    "        with tqdm(range(params['epochs'])) as t:\n",
    "            for epoch in t:\n",
    "\n",
    "                t.set_description('Epoch %d' % epoch)    \n",
    "\n",
    "                start = time.time()\n",
    "                    \n",
    "                epoch_train_loss, optimizer = train_epoch(model, optimizer, device, graph, train_edges, params['batch_size'], epoch, monet_pseudo)\n",
    "                \n",
    "                epoch_train_hits, epoch_val_hits, epoch_test_hits = evaluate_network(\n",
    "                    model, device, graph, train_edges, val_edges, val_edges_neg, test_edges, test_edges_neg, evaluator, params['batch_size'], epoch, monet_pseudo)\n",
    "                \n",
    "                epoch_train_losses.append(epoch_train_loss)\n",
    "                epoch_train_hits.append(epoch_train_hits)\n",
    "                epoch_val_hits.append(epoch_val_hits)\n",
    "\n",
    "                writer.add_scalar('train/_loss', epoch_train_loss, epoch)\n",
    "                \n",
    "                writer.add_scalar('train/_hits@10', epoch_train_hits[0]*100, epoch)\n",
    "                writer.add_scalar('train/_hits@50', epoch_train_hits[1]*100, epoch)\n",
    "                writer.add_scalar('train/_hits@100', epoch_train_hits[2]*100, epoch)\n",
    "                \n",
    "                writer.add_scalar('val/_hits@10', epoch_val_hits[0]*100, epoch)\n",
    "                writer.add_scalar('val/_hits@50', epoch_val_hits[1]*100, epoch)\n",
    "                writer.add_scalar('val/_hits@100', epoch_val_hits[2]*100, epoch)\n",
    "                \n",
    "                writer.add_scalar('test/_hits@10', epoch_test_hits[0]*100, epoch)\n",
    "                writer.add_scalar('test/_hits@50', epoch_test_hits[1]*100, epoch)\n",
    "                writer.add_scalar('test/_hits@100', epoch_test_hits[2]*100, epoch)\n",
    "                \n",
    "                writer.add_scalar('learning_rate', optimizer.param_groups[0]['lr'], epoch)   \n",
    "\n",
    "                t.set_postfix(time=time.time()-start, lr=optimizer.param_groups[0]['lr'],\n",
    "                              train_loss=epoch_train_loss, train_hits=epoch_train_hits[1], \n",
    "                              val_hits=epoch_val_hits[1], test_hits=epoch_test_hits[1]) \n",
    "\n",
    "                per_epoch_time.append(time.time()-start)\n",
    "\n",
    "                # Saving checkpoint\n",
    "                ckpt_dir = os.path.join(root_ckpt_dir, \"RUN_\")\n",
    "                if not os.path.exists(ckpt_dir):\n",
    "                    os.makedirs(ckpt_dir)\n",
    "                torch.save(model.state_dict(), '{}.pkl'.format(ckpt_dir + \"/epoch_\" + str(epoch)))\n",
    "\n",
    "                files = glob.glob(ckpt_dir + '/*.pkl')\n",
    "                for file in files:\n",
    "                    epoch_nb = file.split('_')[-1]\n",
    "                    epoch_nb = int(epoch_nb.split('.')[0])\n",
    "                    if epoch_nb < epoch-1:\n",
    "                        os.remove(file)\n",
    "\n",
    "                scheduler.step(epoch_val_hits[1])\n",
    "\n",
    "                if optimizer.param_groups[0]['lr'] < params['min_lr']:\n",
    "                    print(\"\\n!! LR EQUAL TO MIN LR SET.\")\n",
    "                    break\n",
    "                    \n",
    "                # Stop training after params['max_time'] hours\n",
    "                if time.time()-t0 > params['max_time']*3600:\n",
    "                    print('-' * 89)\n",
    "                    print(\"Max_time for training elapsed {:.2f} hours, so stopping\".format(params['max_time']))\n",
    "                    break\n",
    "    \n",
    "    except KeyboardInterrupt:\n",
    "        print('-' * 89)\n",
    "        print('Exiting from training early because of KeyboardInterrupt')\n",
    "    \n",
    "    \n",
    "    train_hits, val_hits, test_hits = evaluate_network(\n",
    "        model, device, graph, train_edges, val_edges, val_edges_neg, test_edges, test_edges_neg, evaluator, params['batch_size'], epoch, monet_pseudo)\n",
    "\n",
    "    print(f\"Test:\\nHits@10: {test_hits[0]*100:.4f}% \\nHits@50: {test_hits[1]*100:.4f}% \\nHits@100: {test_hits[2]*100:.4f}% \\n\")\n",
    "    print(f\"Train:\\nHits@10: {train_hits[0]*100:.4f}% \\nHits@50: {train_hits[1]*100:.4f}% \\nHits@100: {train_hits[2]*100:.4f}% \\n\")\n",
    "    print(\"Convergence Time (Epochs): {:.4f}\".format(epoch))\n",
    "    print(\"TOTAL TIME TAKEN: {:.4f}s\".format(time.time()-t0))\n",
    "    print(\"AVG TIME PER EPOCH: {:.4f}s\".format(np.mean(per_epoch_time)))\n",
    "\n",
    "    writer.close()\n",
    "\n",
    "    \"\"\"\n",
    "        Write the results in out_dir/results folder\n",
    "    \"\"\"\n",
    "    with open(write_file_name + '.txt', 'w') as f:\n",
    "        f.write(\"\"\"Dataset: {},\\nModel: {}\\n\\nparams={}\\n\\nnet_params={}\\n\\n{}\\n\\nTotal Parameters: {}\\n\\n\n",
    "    FINAL RESULTS\\nTEST HITS@10: {:.4f}\\nTEST HITS@50: {:.4f}\\nTEST HITS@100: {:.4f}\\nTRAIN HITS@10: {:.4f}\\nTRAIN HITS@50: {:.4f}\\nTRAIN HITS@100: {:.4f}\\n\\n\n",
    "    Convergence Time (Epochs): {:.4f}\\nTotal Time Taken: {:.4f}hrs\\nAverage Time Per Epoch: {:.4f}s\\n\\n\\n\"\"\"\\\n",
    "          .format(DATASET_NAME, MODEL_NAME, params, net_params, model, net_params['total_param'],\n",
    "                  test_hits[0]*100, test_hits[1]*100, test_hits[2]*100, train_hits[0]*100, train_hits[1]*100, train_hits[2]*100,\n",
    "                  epoch, (time.time()-t0)/3600, np.mean(per_epoch_time)))\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def main(notebook_mode=False,config=None):\n",
    "    \n",
    "    \"\"\"\n",
    "        USER CONTROLS\n",
    "    \"\"\"\n",
    "    \n",
    "    # terminal mode\n",
    "    if notebook_mode==False:\n",
    "        \n",
    "        parser = argparse.ArgumentParser()\n",
    "        parser.add_argument('--config', help=\"Please give a config.json file with training/model/data/param details\")\n",
    "        parser.add_argument('--gpu_id', help=\"Please give a value for gpu id\")\n",
    "        parser.add_argument('--model', help=\"Please give a value for model name\")\n",
    "        parser.add_argument('--dataset', help=\"Please give a value for dataset name\")\n",
    "        parser.add_argument('--out_dir', help=\"Please give a value for out_dir\")\n",
    "        parser.add_argument('--seed', help=\"Please give a value for seed\")\n",
    "        parser.add_argument('--epochs', help=\"Please give a value for epochs\")\n",
    "        parser.add_argument('--batch_size', help=\"Please give a value for batch_size\")\n",
    "        parser.add_argument('--init_lr', help=\"Please give a value for init_lr\")\n",
    "        parser.add_argument('--lr_reduce_factor', help=\"Please give a value for lr_reduce_factor\")\n",
    "        parser.add_argument('--lr_schedule_patience', help=\"Please give a value for lr_schedule_patience\")\n",
    "        parser.add_argument('--min_lr', help=\"Please give a value for min_lr\")\n",
    "        parser.add_argument('--weight_decay', help=\"Please give a value for weight_decay\")\n",
    "        parser.add_argument('--print_epoch_interval', help=\"Please give a value for print_epoch_interval\")    \n",
    "        parser.add_argument('--L', help=\"Please give a value for L\")\n",
    "        parser.add_argument('--hidden_dim', help=\"Please give a value for hidden_dim\")\n",
    "        parser.add_argument('--out_dim', help=\"Please give a value for out_dim\")\n",
    "        parser.add_argument('--residual', help=\"Please give a value for residual\")\n",
    "        parser.add_argument('--edge_feat', help=\"Please give a value for edge_feat\")\n",
    "        parser.add_argument('--readout', help=\"Please give a value for readout\")\n",
    "        parser.add_argument('--kernel', help=\"Please give a value for kernel\")\n",
    "        parser.add_argument('--n_heads', help=\"Please give a value for n_heads\")\n",
    "        parser.add_argument('--gated', help=\"Please give a value for gated\")\n",
    "        parser.add_argument('--in_feat_dropout', help=\"Please give a value for in_feat_dropout\")\n",
    "        parser.add_argument('--dropout', help=\"Please give a value for dropout\")\n",
    "        parser.add_argument('--layer_norm', help=\"Please give a value for layer_norm\")\n",
    "        parser.add_argument('--batch_norm', help=\"Please give a value for batch_norm\")\n",
    "        parser.add_argument('--sage_aggregator', help=\"Please give a value for sage_aggregator\")\n",
    "        parser.add_argument('--data_mode', help=\"Please give a value for data_mode\")\n",
    "        parser.add_argument('--num_pool', help=\"Please give a value for num_pool\")\n",
    "        parser.add_argument('--gnn_per_block', help=\"Please give a value for gnn_per_block\")\n",
    "        parser.add_argument('--embedding_dim', help=\"Please give a value for embedding_dim\")\n",
    "        parser.add_argument('--pool_ratio', help=\"Please give a value for pool_ratio\")\n",
    "        parser.add_argument('--linkpred', help=\"Please give a value for linkpred\")\n",
    "        parser.add_argument('--cat', help=\"Please give a value for cat\")\n",
    "        parser.add_argument('--self_loop', help=\"Please give a value for self_loop\")\n",
    "        parser.add_argument('--max_time', help=\"Please give a value for max_time\")\n",
    "        parser.add_argument('--layer_type', help=\"Please give a value for layer_type (for GAT and GatedGCN only)\")\n",
    "        parser.add_argument('--pos_enc_dim', help=\"Please give a value for pos_enc_dim\")\n",
    "        parser.add_argument('--pos_enc', help=\"Please give a value for pos_enc\")\n",
    "        args = parser.parse_args()\n",
    "        with open(args.config) as f:\n",
    "            config = json.load(f)\n",
    "            \n",
    "        \n",
    "        # device\n",
    "        if args.gpu_id is not None:\n",
    "            config['gpu']['id'] = int(args.gpu_id)\n",
    "            config['gpu']['use'] = True\n",
    "        device = gpu_setup(config['gpu']['use'], config['gpu']['id'])\n",
    "\n",
    "        # model, dataset, out_dir\n",
    "        if args.model is not None:\n",
    "            MODEL_NAME = args.model\n",
    "        else:\n",
    "            MODEL_NAME = config['model']\n",
    "        if args.dataset is not None:\n",
    "            DATASET_NAME = args.dataset\n",
    "        else:\n",
    "            DATASET_NAME = config['dataset']\n",
    "        dataset = LoadData(DATASET_NAME)\n",
    "        if args.out_dir is not None:\n",
    "            out_dir = args.out_dir\n",
    "        else:\n",
    "            out_dir = config['out_dir']\n",
    "\n",
    "        # parameters\n",
    "        params = config['params']\n",
    "        if args.seed is not None:\n",
    "            params['seed'] = int(args.seed)\n",
    "        if args.epochs is not None:\n",
    "            params['epochs'] = int(args.epochs)\n",
    "        if args.batch_size is not None:\n",
    "            params['batch_size'] = int(args.batch_size)\n",
    "        if args.init_lr is not None:\n",
    "            params['init_lr'] = float(args.init_lr)\n",
    "        if args.lr_reduce_factor is not None:\n",
    "            params['lr_reduce_factor'] = float(args.lr_reduce_factor)\n",
    "        if args.lr_schedule_patience is not None:\n",
    "            params['lr_schedule_patience'] = int(args.lr_schedule_patience)\n",
    "        if args.min_lr is not None:\n",
    "            params['min_lr'] = float(args.min_lr)\n",
    "        if args.weight_decay is not None:\n",
    "            params['weight_decay'] = float(args.weight_decay)\n",
    "        if args.print_epoch_interval is not None:\n",
    "            params['print_epoch_interval'] = int(args.print_epoch_interval)\n",
    "        if args.max_time is not None:\n",
    "            params['max_time'] = float(args.max_time)\n",
    "\n",
    "        # network parameters\n",
    "        net_params = config['net_params']\n",
    "        net_params['device'] = device\n",
    "        net_params['gpu_id'] = config['gpu']['id']\n",
    "        net_params['batch_size'] = params['batch_size']\n",
    "        if args.L is not None:\n",
    "            net_params['L'] = int(args.L)\n",
    "        if args.hidden_dim is not None:\n",
    "            net_params['hidden_dim'] = int(args.hidden_dim)\n",
    "        if args.out_dim is not None:\n",
    "            net_params['out_dim'] = int(args.out_dim)   \n",
    "        if args.residual is not None:\n",
    "            net_params['residual'] = True if args.residual=='True' else False\n",
    "        if args.edge_feat is not None:\n",
    "            net_params['edge_feat'] = True if args.edge_feat=='True' else False\n",
    "        if args.readout is not None:\n",
    "            net_params['readout'] = args.readout\n",
    "        if args.kernel is not None:\n",
    "            net_params['kernel'] = int(args.kernel)\n",
    "        if args.n_heads is not None:\n",
    "            net_params['n_heads'] = int(args.n_heads)\n",
    "        if args.gated is not None:\n",
    "            net_params['gated'] = True if args.gated=='True' else False\n",
    "        if args.in_feat_dropout is not None:\n",
    "            net_params['in_feat_dropout'] = float(args.in_feat_dropout)\n",
    "        if args.dropout is not None:\n",
    "            net_params['dropout'] = float(args.dropout)\n",
    "        if args.layer_norm is not None:\n",
    "            net_params['layer_norm'] = True if args.layer_norm=='True' else False\n",
    "        if args.batch_norm is not None:\n",
    "            net_params['batch_norm'] = True if args.batch_norm=='True' else False\n",
    "        if args.sage_aggregator is not None:\n",
    "            net_params['sage_aggregator'] = args.sage_aggregator\n",
    "        if args.data_mode is not None:\n",
    "            net_params['data_mode'] = args.data_mode\n",
    "        if args.num_pool is not None:\n",
    "            net_params['num_pool'] = int(args.num_pool)\n",
    "        if args.gnn_per_block is not None:\n",
    "            net_params['gnn_per_block'] = int(args.gnn_per_block)\n",
    "        if args.embedding_dim is not None:\n",
    "            net_params['embedding_dim'] = int(args.embedding_dim)\n",
    "        if args.pool_ratio is not None:\n",
    "            net_params['pool_ratio'] = float(args.pool_ratio)\n",
    "        if args.linkpred is not None:\n",
    "            net_params['linkpred'] = True if args.linkpred=='True' else False\n",
    "        if args.cat is not None:\n",
    "            net_params['cat'] = True if args.cat=='True' else False\n",
    "        if args.self_loop is not None:\n",
    "            net_params['self_loop'] = True if args.self_loop=='True' else False\n",
    "        if args.layer_type is not None:\n",
    "            net_params['layer_type'] = layer_type\n",
    "        if args.pos_enc is not None:\n",
    "            net_params['pos_enc'] = True if args.pos_enc=='True' else False\n",
    "        if args.pos_enc_dim is not None:\n",
    "            net_params['pos_enc_dim'] = int(args.pos_enc_dim)\n",
    " \n",
    "\n",
    "    # notebook mode\n",
    "    if notebook_mode:\n",
    "        \n",
    "        # parameters\n",
    "        params = config['params']\n",
    "        \n",
    "        # dataset\n",
    "        DATASET_NAME = config['dataset']\n",
    "        dataset = LoadData(DATASET_NAME)\n",
    "        \n",
    "        # device\n",
    "        device = gpu_setup(config['gpu']['use'], config['gpu']['id'])\n",
    "        out_dir = config['out_dir']\n",
    "        \n",
    "        # GNN model\n",
    "        MODEL_NAME = config['model']\n",
    "        \n",
    "        # network parameters\n",
    "        net_params = config['net_params']\n",
    "        net_params['device'] = device\n",
    "        net_params['gpu_id'] = config['gpu']['id']\n",
    "        net_params['batch_size'] = params['batch_size']\n",
    "      \n",
    "    \n",
    "    # COLLAB\n",
    "    net_params['in_dim'] = dataset.graph.ndata['feat'].shape[-1]\n",
    "    net_params['in_dim_edge'] = dataset.graph.edata['feat'].shape[-1]\n",
    "    net_params['n_classes'] = 1  # binary prediction\n",
    "    \n",
    "    root_log_dir = out_dir + 'logs/' + MODEL_NAME + \"_\" + DATASET_NAME + \"_GPU\" + str(config['gpu']['id']) + \"_\" + time.strftime('%Hh%Mm%Ss_on_%b_%d_%Y')\n",
    "    root_ckpt_dir = out_dir + 'checkpoints/' + MODEL_NAME + \"_\" + DATASET_NAME + \"_GPU\" + str(config['gpu']['id']) + \"_\" + time.strftime('%Hh%Mm%Ss_on_%b_%d_%Y')\n",
    "    write_file_name = out_dir + 'results/result_' + MODEL_NAME + \"_\" + DATASET_NAME + \"_GPU\" + str(config['gpu']['id']) + \"_\" + time.strftime('%Hh%Mm%Ss_on_%b_%d_%Y')\n",
    "    write_config_file = out_dir + 'configs/config_' + MODEL_NAME + \"_\" + DATASET_NAME + \"_GPU\" + str(config['gpu']['id']) + \"_\" + time.strftime('%Hh%Mm%Ss_on_%b_%d_%Y')\n",
    "    dirs = root_log_dir, root_ckpt_dir, write_file_name, write_config_file\n",
    "\n",
    "    if not os.path.exists(out_dir + 'results'):\n",
    "        os.makedirs(out_dir + 'results')\n",
    "        \n",
    "    if not os.path.exists(out_dir + 'configs'):\n",
    "        os.makedirs(out_dir + 'configs')\n",
    "\n",
    "    net_params['total_param'] = view_model_param(MODEL_NAME, net_params)\n",
    "    train_val_pipeline(MODEL_NAME, dataset, params, net_params, dirs)\n",
    "\n",
    "    \n",
    "    \n",
    "if notebook_mode==True:\n",
    "    \n",
    "    config = {}\n",
    "    # gpu config\n",
    "    gpu = {}\n",
    "    gpu['use'] = use_gpu\n",
    "    gpu['id'] = gpu_id\n",
    "    config['gpu'] = gpu\n",
    "    # GNN model, dataset, out_dir\n",
    "    config['model'] = MODEL_NAME\n",
    "    config['dataset'] = DATASET_NAME\n",
    "    config['out_dir'] = out_dir\n",
    "    # parameters\n",
    "    params = {}\n",
    "    params['seed'] = seed\n",
    "    params['epochs'] = epochs\n",
    "    params['batch_size'] = batch_size\n",
    "    params['init_lr'] = init_lr\n",
    "    params['lr_reduce_factor'] = lr_reduce_factor \n",
    "    params['lr_schedule_patience'] = lr_schedule_patience\n",
    "    params['min_lr'] = min_lr\n",
    "    params['weight_decay'] = weight_decay\n",
    "    params['print_epoch_interval'] = 5\n",
    "    params['max_time'] = max_time\n",
    "    config['params'] = params\n",
    "    # network parameters\n",
    "    config['net_params'] = net_params\n",
    "    \n",
    "    # convert to .py format\n",
    "    from utils.cleaner_main import *\n",
    "    cleaner_main('main_COLLAB_edge_classification')\n",
    "    \n",
    "    main(True,config)\n",
    "    \n",
    "else:\n",
    "    \n",
    "    main()\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
